package com.justgame.project.cloud.common.util;

import com.alibaba.fastjson.JSONObject;
import com.justgame.project.cloud.base.entity.WeChatAuthResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.UUID;

@Component
public class WeChatUtils {
    private static final String WX_OAUTH_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
    private static final String WX_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
    private static final String WX_REQUEST_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    private static final String WX_ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
    private static final String WX_ACCESS_TOKEN_LOCK_KEY = "WeChatUtils.getAccessToken";

    private static String APP_ID;
    private static String APP_SECRET;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private RedisDistributedLock redisDistributedLock;

    @Value("${wx.appid}")
    public void setAppId(String appId){
        APP_ID = appId;
    }

    @Value("${wx.secret}")
    public void setAppSecret(String appSecret){
        APP_SECRET = appSecret;
    }

    /**
     * 获取微信授权用户信息地址
     * @param jsCode
     * @return
     */
    public static String getOauthUrl(String jsCode){
        return String.format(WX_OAUTH_URL,APP_ID,APP_SECRET,jsCode);
    }

    /**
     * 获取微信授权手机号
     */
    public static java.lang.String getWxPhoneNumberUrl(String accessToken) {
        return String.format(WX_PHONE_NUMBER_URL,accessToken);
    }

    public static String getWxRequestAccessTokenUrl() {
        return String.format(WX_REQUEST_ACCESS_TOKEN_URL,APP_ID,APP_SECRET);
    }

    public static WeChatAuthResult getWeChatAuthResult(String jsCode){
        String wxAuthUrl = getOauthUrl(jsCode);
        RestTemplate restTemplate = AppContextUtils.getBean(RestTemplate.class);
        String jsonStr = restTemplate.getForObject(wxAuthUrl,String.class);
        WeChatAuthResult result = JSONObject.parseObject(jsonStr,WeChatAuthResult.class);
        Assert.notNull(result,"微信授权登录失败");
        return result;
    }

    public static String getPhoneNumber(String jsCode){
        String wxAuthUrl = getWxPhoneNumberUrl(getAccessToken());
        HttpEntity<Map<String, String>> postForm = new HttpEntity<>(
                MapBuilder.<String,String>builder()
                        .put("code",jsCode)
                        .build(),
                new HttpHeaders());
        RestTemplate restTemplate = AppContextUtils.getBean(RestTemplate.class);
        String jsonStr = restTemplate.postForObject(wxAuthUrl,postForm,String.class);
        JSONObject json = JSONObject.parseObject(jsonStr,JSONObject.class);
        Assert.notNull(json,"微信授权登录失败");
        return json.getObject("phone_info",JSONObject.class).getObject("phone_info",String.class);
    }

    private static Map<String,Long> requestWxAccessToken(){
        String requestAccessTokenUrl = getWxRequestAccessTokenUrl();
        RestTemplate restTemplate = AppContextUtils.getBean(RestTemplate.class);
        String jsonStr = restTemplate.getForObject(requestAccessTokenUrl,String.class);
        JSONObject json = JSONObject.parseObject(jsonStr,JSONObject.class);
        Assert.notNull(json,"微信验证失败");
        return MapBuilder.<String,Long>builder()
                .put(json.getObject("access_token",String.class),json.getObject("expires_in",Long.class))
                .build();
    }


    public static String getAccessToken(){
        RedisDistributedLock redisLock = AppContextUtils.getBean(RedisDistributedLock.class);
        String requestId = UUID.randomUUID().toString();
        Boolean isLocked = null;
        String accessToken = null;
        try {
            if (isLocked = redisLock.tryLock(WX_ACCESS_TOKEN_LOCK_KEY,requestId,500,100,10)){
                RedisUtils redisUtils = AppContextUtils.getBean(RedisUtils.class);
                accessToken = redisUtils.get(WX_ACCESS_TOKEN_KEY);
                if (StringUtils.hasLength(accessToken)){
                    return accessToken;
                }
                Map<String,Long> accessMap = requestWxAccessToken();
                accessToken = accessMap.keySet().stream().findFirst().orElseThrow();
                redisUtils.set(
                        WX_ACCESS_TOKEN_KEY,
                        accessToken,
                        (accessMap.values().stream().findFirst().orElseThrow() - 100L)
                );
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redisLock.releaseLock(WX_ACCESS_TOKEN_LOCK_KEY,requestId);
        }
        return accessToken;
    }
}
